package com.vitalc.biz.aspect;

import com.alibaba.fastjson.JSONObject;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * @author chenzihao
 * @date 2020/12/14
 * @description 日志切面
 */
@Component
@Aspect
public class AopLoggerAspect {

    private static final Logger logger = LoggerFactory.getLogger(AopLoggerAspect.class);

    @Pointcut("execution(public * com.vitalc.biz.*.api.*Controller.*(..))")
    public void pointCut() {
        
    }

    @AfterThrowing(pointcut = "pointCut()", throwing = "throwable")
    public void afterThrowing(JoinPoint joinPoint, Throwable throwable) {
        String methodName = joinPoint.getSignature().getName();
        logger.info("Method Name: [" + methodName + "] ---> AOP after throwing , and throwable message is : " + throwable.getMessage());
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        String classType = joinPoint.getTarget().getClass().getName();
        Class<?> clazz = Class.forName(classType);
        String clazzName = clazz.getName();
        // 获取请求方法名
        String methodName = joinPoint.getSignature().getName();
        // 获取请求参数
        Object[] args = joinPoint.getArgs();
        Map nameAndArgs = getFieldsName(this.getClass(), clazzName, methodName, args);
        
        logger.info("Method Name : [" + methodName + "] ---> Args : " + nameAndArgs.toString());
        long startTimeMillis = System.currentTimeMillis();
        // 调用 proceed() 方法才会真正的执行实际被代理方法
        Object result = joinPoint.proceed();
        long execTimeMillis = System.currentTimeMillis() - startTimeMillis;
        logger.info("Method Name : [" + methodName + "] ---> AOP method exec time millis : " + execTimeMillis);
        logger.info("Method Name : [" + methodName + "] ---> AOP around end , and result is : " + (result != null ? JSONObject.toJSONString(result) : ""));
        return result;
    }

    /**
     * 通过反射机制, 获取被切方法的参数名及参数值
     * @param cls
     * @param clazzName
     * @param methodName
     * @param args
     * @return
     * @throws NotFoundException
     */
    private Map getFieldsName(Class cls, String clazzName, String methodName, Object[] args) throws NotFoundException {
        Map map = new HashMap();
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(new ClassClassPath(cls));
        CtClass cc = pool.get(clazzName);
        CtMethod cm = cc.getDeclaredMethod(methodName);
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
            // exception
        }
        // String[] paramNames = new String[cm.getParameterTypes().length];
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        for (int i = 0; i < cm.getParameterTypes().length; i++) {
            // paramNames即参数名
            map.put(attr.variableName(i + pos), args[i]);
        }
        return map;
    }


}
